#pragma once

#include<queue>
#include<pthread.h>

using std::queue;

#define CAPACITY 10000

template<class T>
class MsgQueue
{
    public:
        MsgQueue()
        {
            capacity_ = CAPACITY;
            pthread_mutex_init(&que_lock_, NULL);
            pthread_cond_init(&prod_cond_, NULL);
            pthread_cond_init(&cons_cond_, NULL);
        }

        ~MsgQueue()
        {
            pthread_mutex_destroy(&que_lock_);
            pthread_cond_destroy(&prod_cond_);
            pthread_cond_destroy(&cons_cond_);
        }

        void Push(const T& msg)
        {
            pthread_mutex_lock(&que_lock_);
            while(que_.size() >= capacity_)
            {
                pthread_cond_wait(&prod_cond_, &que_lock_);
            }
            que_.push(msg);
            pthread_mutex_unlock(&que_lock_);

            pthread_cond_signal(&cons_cond_);
        }

        void Pop(T* msg)
        {
            pthread_mutex_lock(&que_lock_);
            while(que_.empty())
            {
                pthread_cond_wait(&cons_cond_, &que_lock_);
            }
            *msg = que_.front();
            que_.pop();
            pthread_mutex_unlock(&que_lock_);
            pthread_cond_signal(&prod_cond_);
        }
        bool IsEmpty()
        {
            pthread_mutex_lock(&que_lock_);
            if(que_.empty())
            {
                pthread_mutex_unlock(&que_lock_);
                return true;
            }
            pthread_mutex_unlock(&que_lock_);
            return false;
        }
    private:
        queue<T> que_;
        size_t capacity_;

        pthread_mutex_t que_lock_;
        pthread_cond_t prod_cond_;
        pthread_cond_t cons_cond_;
};
